package com.rew.navi.Dijkstra;

import com.rew.navi.managers.imp.GMapManager;
import com.rew.navi.models.GMap;
import com.rew.navi.models.GNode;

import java.util.ArrayList;
import java.util.List;

import static java.lang.Math.abs;

/**
 * .
 * <p>
 * Created by HuiWen Ren on 2016/9/2.
 */
public class MapBuilder {


    private static final int BREAK = -1;

    private int num = 0;


    private GMap aimMap; // 目标地图
    private GNode stPoint; // 起始点
    private GNode enPoint; // 终止点

    private List<GNode> mainPoint; // 地图结点
    private List<Area> unArea; // 禁区

    private class Area { // 区域类

        public Area(GNode ul, GNode ur,
                    GNode dr, GNode dl) {
            this.ul = ul;
            this.ur = ur;
            this.dr = dr;
            this.dl = dl;
        }

        GNode ul;
        GNode ur;
        GNode dl;
        GNode dr;
    }


    public MapBuilder(GMap gMap, GNode stPoint, GNode enPoint) {
        aimMap = gMap;
        this.stPoint = stPoint;
        this.enPoint = enPoint;
    }

    /**
     * 求正切值.
     *
     * @param x .
     * @param y .
     * @return tan
     */
    private double tan(GNode x, GNode y) {
        double cX = abs(x.getX() - y.getX());
        double cY = abs(x.getY() - y.getY());
        return cY / cX;
    }

    /**
     * 相交.
     *
     * @param st 起点
     * @param en 终点
     * @param x  起点
     * @param y  终点
     * @return 相交返回true
     */
    private boolean cross(GNode st, GNode en, GNode x, GNode y) {
        double stan = tan(st, en);
        double xtan = tan(st, x);
        double ytan = tan(st, y);
        return (stan > xtan && stan < ytan) ||
                (stan < xtan && stan > ytan);
    }

    /**
     * 判断直线与区域是否相交.
     *
     * @param st  直线起点
     * @param en  直线终点
     * @param are 待判断区域
     * @return 不相交返回true
     */

    private boolean noCross(GNode st, GNode en,
                            Area are) {

        return !(cross(st, en, are.ul, are.ur) || cross(st, en, are.ur, are.dr) ||
                cross(st, en, are.dl, are.dr) || cross(st, en, are.ul, are.dl));
    }

    private double length(GNode x, GNode y) {
        int dx = abs(x.getX() - y.getX());
        int dy = abs(x.getY() - y.getY());
        int xx = dx * dx;
        int yy = dy * dy;
        return Math.sqrt(xx + yy);
    }

    /**
     * 地图初始化.
     */
    private void iniMap() {
        mainPoint = new ArrayList<>();
        String[] map = aimMap.getInfo().split("，");
        for (int i = 0; i < map.length; i += 2) {
            int x = Integer.valueOf(map[i]);
            int y = Integer.valueOf(map[i + 1]);
            GNode tmp = new GNode(x, y);
            mainPoint.add(tmp);
        }
        mainPoint.add(stPoint);
        mainPoint.add(enPoint);

        num = (map.length + 1) / 2 + 2;

        unArea = new ArrayList<>();
        String[] are = aimMap.getArea().split("，");
        for (int i = 0; i < are.length; i += 8) {
            int ulx = Integer.valueOf(are[i]);
            int uly = Integer.valueOf(are[i + 1]);
            int urx = Integer.valueOf(are[i + 2]);
            int ury = Integer.valueOf(are[i + 3]);
            int drx = Integer.valueOf(are[i + 4]);
            int dry = Integer.valueOf(are[i + 5]);
            int dlx = Integer.valueOf(are[i + 6]);
            int dly = Integer.valueOf(are[i + 7]);
            GNode ul = new GNode(ulx, uly);
            GNode ur = new GNode(urx, ury);
            GNode dr = new GNode(drx, dry);
            GNode dl = new GNode(dlx, dly);
            Area tmpArea = new Area(ul, ur, dr, dl);
            unArea.add(tmpArea);
        }
    }

    /**
     * 地图处理.
     *
     * @return 邻接矩阵
     */
    public int[][] mapBuild() {
        if (aimMap.getSqr() != null){
            return readSqr(aimMap.getSqr());
        } else {
            iniMap();
            // n个点依次编号为 0~num-1
            int[][] IMapSqr = new int[num][num];
            for (int i = 0; i < num; i++) {
                for (int j = 0; j < num; j++) {
                    boolean cross = false;
                    for (Area anUnArea : unArea) {
                        if (!noCross(mainPoint.get(i),
                                mainPoint.get(j),
                                anUnArea)) {
                            cross = true; // 相交
                        }
                    }
                    if (!cross) { // 未相交
                        IMapSqr[i][j] = (int) length(mainPoint.get(i), mainPoint.get(j));
                    } else {
                        IMapSqr[i][j] = BREAK;
                    }
                }
            }
            addMapSqr(IMapSqr);
            return IMapSqr;
        }
    }

    private void addMapSqr(int[][] IMapSqr) {
        int len = num - 2;
        String newSqr = Integer.toString(len) + " ";
        for (int i = 0; i < len; i++){
            for (int j = 0; j < len; j++){
                newSqr += Integer.toString(IMapSqr[i][j]) + " ";
            }
        }
        GMapManager gMapManager = new GMapManager();
        gMapManager.addMapInfo(aimMap.getName(), aimMap.getInfo(),
                aimMap.getURL(), aimMap.getArea(), newSqr);
    }

    /**
     * 找到对应点实际坐标.
     *
     * @param i 序号
     * @return 坐标点
     */
    GNode find(int i) {
        if (i < num) {
            return mainPoint.get(i);
        } else {
            return null;
        }
    }

    /**
     * getter.
     *
     * @return num
     */
    int getNum() {
        return num;
    }

    /**
     * 读取邻接表.
     *
     * @param mapSqr 邻接表串
     * @return 邻接表
     */
    private int[][] readSqr(String mapSqr) {
        String[] tmp = mapSqr.split(" ");
        int len = Integer.valueOf(tmp[0]);
        int[][] ans = new int[len][len];
        int cot = 0;
        for (int i = 0; i < len; i++){
            for (int j = 0; j < len; j++){
                ans[i][j] = Integer.valueOf(tmp[++cot]);
            }
        }
        return ans;
    }
}
